package cn.trigram;

import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_NATIVE;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class MethodReplaceInvokeVisitor extends ClassVisitor {

  private final String oldOwner;

  private final String oldMethodName;

  private final String oldMethodDesc;

  private final int newOpcode;

  private final String newOwner;

  private final String newMethodName;

  private final String newMethodDesc;

  private final long timestamp;

  public MethodReplaceInvokeVisitor(ClassVisitor classVisitor, long timestamp) {

    super(Opcodes.ASM7, classVisitor);
    this.oldOwner      = "java/util/Date";
    this.oldMethodName = "<init>";
    this.oldMethodDesc = "()V";

    this.newOpcode     = Opcodes.INVOKESPECIAL;
    this.newOwner      = "java/util/Date";
    this.newMethodName = "<init>";
    this.newMethodDesc = "(J)V";
    this.timestamp     = timestamp;
  }

  @Override
  public MethodVisitor visitMethod(
      int access, String name, String descriptor, String signature, String[] exceptions
  ) {

    MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
    if (mv != null && !"<init>".equals(name) && !"<clinit>".equals(name)) {
      boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;
      boolean isNativeMethod   = (access & ACC_NATIVE) != 0;
      if (!isAbstractMethod && !isNativeMethod) {
        mv = new MethodReplaceInvokeAdapter(api, mv, this.timestamp);
      }
    }
    return mv;
  }

  private class MethodReplaceInvokeAdapter extends MethodVisitor {

    private final long timestamp;

    public MethodReplaceInvokeAdapter(int api, MethodVisitor methodVisitor, long timestamp) {

      super(api, methodVisitor);
      this.timestamp = timestamp;
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {

      if (oldOwner.equals(owner) && oldMethodName.equals(name) && oldMethodDesc.equals(descriptor)) {
        // 注意，最后一个参数是false，会不会太武断呢？
        super.visitLdcInsn(timestamp);
        super.visitMethodInsn(newOpcode, newOwner, newMethodName, newMethodDesc, false);
      } else {
        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
      }
    }

  }

}